package com.wlyuan.open.core.converter;

import com.wlyuan.open.core.converter.conversions.SimplePushMessageConversion;
import com.wlyuan.open.core.converter.conversions.PushMessageConversion;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author yuanjie
 */
@Slf4j
public class PushMessageConverter {
    private final Map<String, PushMessageConversion> conversions = new HashMap<>();

    private boolean supportConversion(ParameterizedType parameterizedType) {
        return PushMessageConversion.class.getTypeName().equals(parameterizedType.getRawType().getTypeName());
    }

    private String getConversionName(Long tenantId, Type type) {
        return tenantId + "-" + type.getTypeName();
    }

    public <T extends PushMessage> Type[] getActualTypeArguments(PushMessageConversion<T> conversion) {
        var types = conversion.getClass().getGenericInterfaces();
        return Arrays.stream(types)
                .filter(type -> type instanceof ParameterizedType)
                .map(type -> (ParameterizedType) type)
                .filter(this::supportConversion)
                .findFirst()
                .map(ParameterizedType::getActualTypeArguments)
                .orElse(null);
    }

    private <T extends PushMessage> PushMessageConversion<T> getConversion(T message) {
        var typeName = message.getClass().getTypeName();
        var conversion = conversions.get(typeName);
        return (PushMessageConversion<T>) conversion;
    }


    public <T extends PushMessage> void addConversion(Long tenantId, PushMessageConversion<T> conversion) {
        var types = getActualTypeArguments(conversion);
        if (null == types || 0 == types.length) {
            logger.error("Invalid event conversion: {}", conversion);
            return;
        }
        var messageType = types[0];
        conversions.put(getConversionName(tenantId, messageType), conversion);
    }

    public <T extends PushMessage> void addConversion(Long tenantId, Class<T> messageClass, Map<String,Object> map) {
        conversions.put(getConversionName(tenantId, messageClass), new SimplePushMessageConversion(map, true));
    }

    public <T extends PushMessage> Object convert(T message) {
        var conversion = getConversion(message);
        if (null == conversion) {
            logger.warn("没有可用的转换器: {}", message);
            return message;
        }
        return conversion.process(message);
    }
}
